GitLab 集成 Docker Registry 本地化配置实战
架构说明
本地化配置将 GitLab 与独立的 Docker Registry 服务通过 docker-compose 进行编排,实现完整的容器镜像管理。核心思路是:GitLab 负责用户管理和 JWT 令牌签发,独立的 Registry 服务负责镜像存储,两者通过 token 认证机制对接。
┌──────────────────────────────────────────────────────┐
│ docker-compose 网络 │
│ │
│ ┌───────────────┐ ┌──────────────────────┐ │
│ │ GitLab │────────>│ Docker Registry │ │
│ │ (gitlab-ce) │ JWT │ (registry:2) │ │
│ │ :80 / :443 │ Auth │ :5000 │ │
│ └───────┬───────┘ └──────────┬───────────┘ │
│ | | │
│ v v │
│ ┌───────────────┐ ┌──────────────────────┐ │
│ │ GitLab DB │ │ auth_token │ │
│ │ (PostgreSQL) │ │ 验证服务 │ │
│ └───────────────┘ └──────────────────────┘ │
│ │
│ 认证流程: │
│ docker client → Registry → GitLab /jwt/auth → JWT │
│ docker client → Registry (携带 token) → 推拉镜像 │
└──────────────────────────────────────────────────────┘
text
认证流程详解
- 用户执行
docker login或docker pull/push - Registry 将请求重定向到 GitLab 的 JWT 认证端点 (
/jwt/auth) - GitLab 验证用户身份后签发 JWT 令牌
- Docker 客户端携带令牌访问 Registry 完成操作
关键配置项
1. GitLab 侧配置
GitLab 需要配置 Registry 的对接参数,告诉 GitLab 外部 Registry 服务的地址和启用状态:
# gitlab.rb 关键配置
registry_external_url 'http://your-gitlab-domain:5050'
# 启用 Registry 功能
gitlab_rails['registry_enabled'] = true
# 注意:使用独立 Registry 容器时,关闭 GitLab 内置 Registry
registry['enable'] = false
ruby
配置要点:
| 参数 | 说明 |
|---|---|
registry_external_url | Registry 对外暴露的 URL,Docker 客户端通过此地址访问 |
gitlab_rails['registry_enabled'] | 在 GitLab Rails 层面启用 Registry 功能 |
registry['enable'] | 设为 false 表示不使用 GitLab 内置 Registry,使用独立容器 |
2. Registry 侧配置
Registry 需要配置与 GitLab 的认证连接,核心是 token 认证(auth token)机制:
# Registry 配置文件(config.yml)
version: 0.1
auth:
token:
realm: http://your-gitlab-domain/jwt/auth # GitLab JWT 认证端点
service: container_registry # 服务标识符
issuer: gitlab-issuer # 令牌签发者
rootcertbundle: /etc/registry/ssl/root.crt # 根证书路径(用于验证令牌)
http:
addr: 0.0.0.0:5000
tls:
certificate: /etc/registry/ssl/registry.crt
key: /etc/registry/ssl/registry.key
storage:
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
yaml
核心参数说明:
| 参数 | 说明 |
|---|---|
auth.token.realm | GitLab JWT 认证端点,对接 GitLab 用户体系,Registry 收到未认证请求时重定向到此地址 |
auth.token.service | 服务标识符,必须与 GitLab 配置中的 registry.token.service 一致 |
auth.token.issuer | 令牌签发者,需与 GitLab 配置中的 registry.token.issuer 一致(默认 gitlab-issuer) |
rootcertbundle | GitLab 签发令牌时使用的根证书的公钥部分,Registry 用它来验证令牌合法性 |
证书配置
为什么需要证书
即使不使用 HTTPS 访问,证书也是必须的。原因:
- 令牌验证需要:Registry 的 auth 服务需要根证书(
rootcertbundle)来验证 GitLab 签发的 JWT 令牌 - 启动不报错:不配置
rootcertbundle时,Registry 启动会报错root cert bundle not configured,无法正常启动 - 内部 TLS 可选:Registry 容器间的 TLS 加密是可选的,但 token 验证所需证书是必须的
生成自签名证书
# 1. 创建证书存储目录
mkdir -p /home/registry/ssl
# 2. 生成 Registry 服务证书(用于 Registry TLS,可选但推荐)
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout /home/registry/ssl/registry.key \
-x509 -days 3650 \
-out /home/registry/ssl/registry.crt \
-subj "/CN=your-gitlab-domain"
# 3. 生成根证书(用于 JWT token 验证,必须配置)
# 这个证书的公钥会被 Registry 用来验证 GitLab 签发的令牌
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout /home/registry/ssl/root.key \
-x509 -days 3650 \
-out /home/registry/ssl/root.crt \
-subj "/CN=gitlab-issuer"
# 4. 验证证书是否正确生成
ls -la /home/registry/ssl/
# 应该看到: registry.crt registry.key root.crt root.key
bash
证书文件用途:
| 文件 | 用途 |
|---|---|
registry.crt + registry.key | Registry 服务 TLS 证书 |
root.crt | 根证书公钥,配置为 rootcertbundle,用于验证 GitLab JWT 令牌签名 |
root.key | 根证书私钥,GitLab 侧用于签发令牌(GitLab 内部使用) |
不配置
rootcertbundle时,Registry 的 auth 服务启动会报错root cert bundle not configured,所以即使不用 HTTPS 也需要生成证书。
docker-compose 完整配置
# docker-compose.yml
version: '3.8'
services:
gitlab:
image: 'gitlab/gitlab-ce:16.1.2-ce.0'
restart: always
hostname: 'gitlab.local'
container_name: gitlab
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab.local'
# 配置 Registry 对外访问地址
registry_external_url 'http://gitlab.local:5050'
# 在 Rails 层启用 Registry
gitlab_rails['registry_enabled'] = true
# 关闭内置 Registry(使用下面的独立容器)
registry['enable'] = false
ports:
- '80:80'
- '443:443'
- '5050:5050'
volumes:
- './gitlab/config:/etc/gitlab'
- './gitlab/data:/var/opt/gitlab'
- './gitlab/logs:/var/log/gitlab'
networks:
- gitlab-network
registry:
image: 'registry:2'
restart: always
container_name: gitlab-registry
ports:
- '5000:5000'
environment:
# === JWT Token 认证配置 ===
# GitLab 认证端点(与 GitLab 服务通信)
REGISTRY_AUTH_TOKEN_REALM: 'http://gitlab.local/jwt/auth'
# 服务标识符
REGISTRY_AUTH_TOKEN_SERVICE: 'container_registry'
# 令牌签发者(必须与 GitLab 配置一致)
REGISTRY_AUTH_TOKEN_ISSUER: 'gitlab-issuer'
# 根证书路径(验证令牌签名)
REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE: '/etc/registry/ssl/root.crt'
# === TLS 证书配置 ===
REGISTRY_HTTP_TLS_CERTIFICATE: '/etc/registry/ssl/registry.crt'
REGISTRY_HTTP_TLS_KEY: '/etc/registry/ssl/registry.key'
# === 存储配置 ===
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: '/var/lib/registry'
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
volumes:
- './registry/data:/var/lib/registry'
# 只读挂载证书目录
- './registry/ssl:/etc/registry/ssl:ro'
networks:
- gitlab-network
depends_on:
- gitlab
networks:
gitlab-network:
driver: bridge
yaml
目录结构
project/
├── docker-compose.yml
├── gitlab/
│ ├── config/ # GitLab 配置(gitlab.rb 等)
│ ├── data/ # GitLab 数据(数据库、仓库等)
│ └── logs/ # GitLab 日志
└── registry/
├── data/ # Registry 镜像存储
│ └── docker/
│ └── registry/
│ └── v2/
└── ssl/
├── registry.crt # Registry TLS 证书
├── registry.key # Registry TLS 私钥
├── root.crt # 根证书公钥(令牌验证)
└── root.key # 根证书私钥(令牌签发)
text
Nginx 反向代理配置(可选)
在生产环境中,通常会在 Registry 前面加一层 Nginx 反向代理,统一处理 SSL 终结(SSL Termination)和域名路由:
# /etc/nginx/conf.d/gitlab-registry.conf
# HTTP -> HTTPS 重定向
server {
listen 80;
server_name registry.example.com;
return 301 https://$host$request_uri;
}
# HTTPS 主配置
server {
listen 443 ssl http2;
server_name registry.example.com;
# SSL 证书配置
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# SSL 安全加固
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Docker 镜像可能很大,不限制上传大小
client_max_body_size 0;
# 关闭访问日志提升性能
access_log off;
# 反向代理到 Registry 容器
location / {
proxy_pass http://registry:5000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Docker Registry V2 API 必需头
proxy_set_header Docker-Distribution-Api-Version registry/2.0;
# 大文件上传超时设置
proxy_read_timeout 900;
proxy_connect_timeout 900;
proxy_send_timeout 900;
# 关闭缓冲,避免大文件上传问题
proxy_buffering off;
proxy_request_buffering off;
}
}
nginx
加入 Nginx 后的 docker-compose:
# docker-compose.yml(加入 Nginx)
services:
# ... gitlab 和 registry 服务同上 ...
nginx:
image: nginx:alpine
container_name: nginx-proxy
restart: always
ports:
- '443:443'
- '80:80'
volumes:
- './nginx/conf.d:/etc/nginx/conf.d:ro'
- './nginx/certs:/etc/nginx/certs:ro'
networks:
- gitlab-network
depends_on:
- registry
yaml
Nginx 关键配置说明:
| 配置项 | 说明 |
|---|---|
client_max_body_size 0 | 不限制上传大小,Docker 镜像层可能数 GB |
proxy_read_timeout 900 | 大镜像推送需要更长超时时间 |
proxy_buffering off | 关闭缓冲避免大文件内存问题 |
X-Forwarded-Proto $scheme | 让 Registry 生成正确的 HTTPS 回调 URL |
启动与验证
启动服务
# 1. 先确保证书已生成
ls -la ./registry/ssl/
# 2. 启动所有服务
docker-compose up -d
# 3. 查看服务状态
docker-compose ps
# 4. 查看 Registry 日志(确认无报错)
docker-compose logs -f registry
# 5. 查看 GitLab 日志
docker-compose logs -f gitlab
bash
验证 Registry 可用性
# 检查 Registry V2 API
curl http://localhost:5000/v2/_catalog
# 预期返回
# {"repositories":[]}
# 检查 Registry 版本信息
curl http://localhost:5000/v2/
# 预期返回(空 JSON 表示需要认证,但服务正常)
# {}
bash
验证 GitLab 与 Registry 连通性
# 在 GitLab Web 界面中检查:
# 1. 进入任意项目
# 2. 左侧菜单应出现 "Packages & Registries" -> "Container Registry"
# 3. 如果能看到 Registry 页面,说明 GitLab 已成功对接 Registry
bash
镜像推拉操作
构建并推送镜像
# 1. 登录 Registry
docker login your-gitlab-domain:5050
# 输入 GitLab 用户名和密码
# Login Succeeded
# 2. 构建镜像(使用 Registry 地址打标签)
docker build -t your-gitlab-domain:5050/root/pkg-demo:1.0.1 .
# 或者给已有镜像打标签
docker tag alpine:latest your-gitlab-domain:5050/root/pkg-demo:1.0.1
# 3. 推送镜像到 Registry
docker push your-gitlab-domain:5050/root/pkg-demo:1.0.1
# 4. 查看推送结果
# 在 GitLab 项目 -> Container Registry 页面中查看镜像
bash
私有仓库操作
私有项目需要先登录才能拉取镜像:
# 登录 Registry
docker login your-gitlab-domain:5050
# 输入 GitLab 用户名和密码
# Login Succeeded
# 拉取镜像
docker pull your-gitlab-domain:5050/root/pkg-demo:1.0.1
bash
公共仓库操作
公共项目无需登录,可直接拉取:
# 直接拉取(无需登录)
docker pull your-gitlab-domain:5050/root/pkg-demo:1.0.1
bash
使用 Access Token 登录(推荐)
对于 CI/CD 自动化场景,推荐使用 GitLab Access Token 代替密码:
# 使用 Personal Access Token 或 Deploy Token
docker login your-gitlab-domain:5050
# Username: <任意非空字符串,如 oauth2 或用户名>
# Password: <你的 GitLab Access Token>
bash
GitLab 支持的认证方式:
| 方式 | Username | Password | 适用场景 |
|---|---|---|---|
| 用户密码 | GitLab 用户名 | GitLab 密码 | 手动操作 |
| Personal Access Token | GitLab 用户名 | Token 值 | 个人自动化 |
| Deploy Token | 任意 | Token 值 | 项目级自动化 |
| CI Job Token | gitlab-ci-token | $CI_JOB_TOKEN | CI/CD 流水线 |
项目可见性设置
| 可见性 | 拉取 | 推送 | 登录要求 |
|---|---|---|---|
| Private | 需登录 | 需登录 | 必须 |
| Internal | 登录用户可拉取 | 需登录 | 必须 |
| Public | 任何人可拉取 | 需登录 | 拉取不需,推送需要 |
修改项目可见性:
- 进入项目 -> Settings > General > Visibility
- 选择 Public / Internal / Private
- 点击 Save changes
实战演示
场景一:私有仓库登录与拉取
# 私有仓库需要先登录
$ docker login 192.168.3.177:5050
Username: root
Password: ********
Login Succeeded
# 登录成功后拉取镜像
$ docker pull 192.168.3.177:5050/root/pkg-demo:1.0.1
1.0.1: Pulling from root/pkg-demo
Digest: sha256:xxxxx
Status: Downloaded newer image for 192.168.3.177:5050/root/pkg-demo:1.0.1
# 拉取成功
bash
场景二:未登录拉取私有仓库(失败)
# 先登出
$ docker logout 192.168.3.177:5050
Removing login credentials for 192.168.3.177:5050
# 尝试拉取私有仓库镜像
$ docker pull 192.168.3.177:5050/root/pkg-demo:1.0.1
Error response from daemon: pull access denied for 192.168.3.177:5050/root/pkg-demo, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
# 拉取失败 - 权限校验不通过
bash
场景三:切换为公共仓库后(无需登录)
# 在 GitLab 项目设置中将项目可见性改为 Public
# Settings > General > Visibility > Public > Save changes
# 无需登录直接拉取
$ docker pull 192.168.3.177:5050/root/pkg-demo:1.0.1
1.0.1: Pulling from root/pkg-demo
Digest: sha256:xxxxx
Status: Image is up to date for 192.168.3.177:5050/root/pkg-demo:1.0.1
# 拉取成功
bash
CI/CD 中使用 Registry
配置完成后,可以在 .gitlab-ci.yml 中自动推送镜像:
# .gitlab-ci.yml 示例
build-and-push:
stage: build
image: docker:24
services:
- docker:24-dind
variables:
# 使用 CI 内置变量自动构建 Registry 地址
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
script:
# 使用 CI Job Token 自动登录(无需手动输入密码)
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
only:
- tags
yaml
GitLab CI 内置变量:
| 变量 | 说明 | 示例值 |
|---|---|---|
$CI_REGISTRY | Registry 地址 | 192.168.3.177:5050 |
$CI_REGISTRY_IMAGE | 项目镜像基础路径 | 192.168.3.177:5050/root/pkg-demo |
$CI_JOB_TOKEN | CI 作业令牌(自动生成) | 自动填充 |
$CI_COMMIT_TAG | 触发的 Git 标签 | v1.0.1 |
常见问题
问题一:Registry 启动报 root cert bundle 未配置
Error: root cert bundle not configured
text
原因:Registry 的 token 认证服务需要根证书来验证 GitLab 签发的 JWT 令牌。
解决方案:生成自签名证书,并在配置中指定 rootcertbundle 路径。
# 生成根证书
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout /home/registry/ssl/root.key \
-x509 -days 3650 \
-out /home/registry/ssl/root.crt \
-subj "/CN=gitlab-issuer"
bash
问题二:Docker 拉取报 HTTPS 错误
Error response from daemon: Get "https://...": server gave HTTP response to HTTPS client
text
原因:Docker 默认要求使用 HTTPS 与 Registry 通信,本地 HTTP 部署需要配置信任。
解决方案:
# 在 Docker 客户端配置信任 HTTP Registry
# 编辑 /etc/docker/daemon.json(不存在则新建)
{
"insecure-registries": ["your-gitlab-domain:5000", "your-gitlab-domain:5050"]
}
# 重启 Docker 服务
sudo systemctl restart docker
# macOS Docker Desktop:
# Preferences > Docker Engine > 添加 insecure-registries > Apply & Restart
bash
问题三:认证失败(token signed by untrusted key)
Error: unauthorized: authentication required
# 或
Error: token signed by untrusted key
text
原因:Registry 的 rootcertbundle 证书与 GitLab 签发令牌时使用的密钥不匹配。
解决方案:
- 确认
auth.token.realm指向正确的 GitLab JWT 端点(http://gitlab-domain/jwt/auth) - 确认
auth.token.issuer与 GitLab 配置中的registry.token.issuer一致(默认gitlab-issuer) - 确认证书文件路径映射正确
- 确认根证书的 CN 与 issuer 配置匹配
问题四:容器内找不到证书
原因:docker-compose 中 volumes 映射配置错误,证书文件未正确挂载。
解决方案:检查 volumes 映射,确保证书文件挂载到容器内正确路径:
volumes:
# 只读挂载证书目录
- './registry/ssl:/etc/registry/ssl:ro'
# 验证挂载是否成功
# docker exec -it gitlab-registry ls -la /etc/registry/ssl/
# 应该看到: registry.crt registry.key root.crt root.key
yaml
问题五:Nginx 代理后推送大镜像超时
Error: write /var/lib/registry/docker/registry/v2/...: no space left on device
# 或
Error: net/http: timeout awaiting response headers
text
解决方案:
- 调整 Nginx 超时配置:
proxy_read_timeout 900;
proxy_connect_timeout 900;
proxy_send_timeout 900;
client_max_body_size 0; # 不限制上传大小
nginx
- 检查 Registry 存储空间是否充足
- 考虑使用对象存储(S3/MinIO)替代本地文件系统
问题六:GitLab 页面看不到 Container Registry 菜单
解决方案:
- 确认
gitlab_rails['registry_enabled'] = true已设置 - 确认
registry_external_url已正确配置 - 在 GitLab 容器内执行
gitlab-ctl reconfigure使配置生效 - 重启 GitLab 容器:
docker-compose restart gitlab
# 进入 GitLab 容器重新加载配置
docker exec -it gitlab gitlab-ctl reconfigure
bash
配置文件参考
课程提供的配置资料包含两个核心部分:
- GitLab 配置:
gitlab.rb中 Registry 对接参数(registry_external_url、registry_enabled) - Registry 配置:
auth_token认证(realm/service/issuer)+ SSL 证书(rootcertbundle)+ volumes 映射
配置完成后,可在 CI/CD 流水线中使用 docker login + docker push 推送镜像到本地 Registry。
课程实际部署配置
以下是课程中实际使用的 GitLab + Registry docker-compose 配置(ARM 架构服务器环境),包含了完整的 GitLab Omnibus 配置和独立 Registry 容器:
version: '3.6'
services:
web:
# x86 架构使用: gitlab/gitlab-ce:latest
# ARM 架构使用:
image: 'zengxs/gitlab:16.1.2-ce-arm64'
restart: always
hostname: '192.168.31.77'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://192.168.31.77:10082'
letsencrypt['contact_emails'] = ['admin@wayearn.com']
gitlab_rails['gitlab_shell_ssh_port'] = 10083
# Registry 对外访问地址
registry_external_url "http://192.168.31.77:5050"
registry_nginx['redirect_http_to_https'] = false
# 启用独立 Registry 容器模式
registry['enable'] = true
registry['host'] = '192.168.31.77'
registry['port'] = '5050'
registry['api_url'] = 'http://registry:5000/'
registry['issuer'] = 'gitlab-issuer'
registry['key'] = '/certs/registry.key'
registry['path'] = '/var/lib/registry'
env_file:
- .env
ports:
- '5050:5050'
- '10082:10082'
- '10083:22'
volumes:
- '$GITLAB_HOME/config:/etc/gitlab'
- '$GITLAB_HOME/logs:/var/log/gitlab'
- '$GITLAB_HOME/data:/var/opt/gitlab'
- /home/registry/certs:/certs
- /home/registry/shared:/var/lib/registry
shm_size: '512m'
registry:
image: registry:2
restart: always
container_name: registry
volumes:
- /home/registry/config.yml:/etc/docker/registry/config.yml
- /home/registry/certs:/certs
- /home/registry/shared:/var/lib/registry
yaml
Registry 配置文件
在服务器上创建 /home/registry/config.yml:
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
auth:
token:
realm: http://192.168.31.77:10082/jwt/auth
service: container_registry
issuer: gitlab-issuer
rootcertbundle: /certs/registry.crt
yaml
证书生成方法
在服务器上执行以下命令生成 Registry 所需的证书文件:
# 创建证书存储目录
mkdir -p /home/registry/certs
cd /home/registry/certs
# 生成随机密码文件
openssl rand -hex -out password_file 32
# 创建 PKCS#10 证书请求
openssl req -new -passout file:password_file -newkey rsa:4096 -batch > registry.csr
# 转换 RSA 密钥(去除密码保护)
openssl rsa -passin file:password_file -in privkey.pem -out registry.key
# 生成自签名证书(有效期约27年)
openssl x509 -in registry.csr -out registry.crt -req -signkey registry.key -days 10000
bash
参考资源
↑